package com.whfc.mach.xxl.job;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.whfc.XxlJobConfig;
import com.whfc.common.constant.QueueConst;
import com.whfc.common.util.DateUtil;
import com.whfc.influx.dao.AppDeviceObdLogDao;
import com.whfc.mach.dao.AppMachMapper;
import com.whfc.mach.dao.AppMaintainMapper;
import com.whfc.mach.dao.AppMaintainRuleMapper;
import com.whfc.mach.dto.AppMachDTO;
import com.whfc.mach.dto.AppMachDaySegDTO;
import com.whfc.mach.dto.obd.AppDeviceObdLogDTO;
import com.whfc.mach.dto.obd.MaintainRuleDTO;
import com.whfc.mach.dto.obd.MaintainWebsocketDTO;
import com.whfc.mach.entity.AppMaintain;
import com.whfc.mach.enums.MaintainRuleType;
import com.whfc.mach.manager.ObdDataMgr;
import com.whfc.mach.manager.util.MachSegmentUtil;
import com.whfc.mach.manager.util.MachUtil;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.annotation.XxlJob;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.stereotype.Component;

import java.util.*;

/**
 * @author sun_guodong
 * @description 检测设备是否需要保养
 * @date 2021-03-08
 */
@Component
@ConditionalOnBean(XxlJobConfig.class)
public class ObdMaintainJob {

    @Autowired
    private AppMaintainRuleMapper maintainRuleMapper;

    @Autowired
    private AppMachMapper machMapper;

    @Autowired
    private AppMaintainMapper maintainMapper;

    @Autowired
    private AppDeviceObdLogDao appDeviceObdLogDao;

    @Autowired
    private ObdDataMgr obdDataMgr;

    @Autowired
    private AmqpTemplate amqpTemplate;
    /**
     * 参数名称
     */
    private static final String PARAM_NAME = "deptIds";

    private static final String WEBSOCKET_CONTENT = "有%s台设备需要保养,请您及时处理";

    /**
     * 检测obd设备是否需要保养
     */
    @XxlJob("checkObdMaintain")
    public void checkObdMaintain() {
        XxlJobHelper.log("定时任务检测设备是否需要保养");
        try {
            String param = XxlJobHelper.getJobParam();
            List<Integer> deptIds = new ArrayList<>();
            JSONObject jsonObject = null;
            if (StringUtils.isNotEmpty(param)) {
                jsonObject = JSONObject.parseObject(param);
            }
            if (jsonObject != null) {
                JSONArray deptIdsJson = jsonObject.getJSONArray(PARAM_NAME);
                if (deptIdsJson != null) {
                    deptIds = deptIdsJson.toJavaList(Integer.class);
                }
            }
            XxlJobHelper.log("设备保养检测服务");
            Map<Integer, List<MaintainRuleDTO>> map = getCheckRules();

            for (Integer deptId : deptIds) {
                List<MaintainRuleDTO> ruleDTOList = map.get(deptId);
                if (ruleDTOList == null || ruleDTOList.size() == 0) {
                    continue;
                }
                List<AppMaintain> list = new ArrayList<>();
                List<AppMachDTO> machList = machMapper.selectObdMach(deptId);
                for (AppMachDTO machDTO : machList) {
                    this.check(list, machDTO, ruleDTOList);
                }

                if (list.size() > 0) {
                    maintainMapper.batchInsert(list);
                }
                Integer cnt = maintainMapper.countByDeptId(deptId);
                XxlJobHelper.log("项目:{}需要保养的设备数:{}", deptId, cnt);

                if (cnt > 0) {
                    // 发送websocket消息
                    MaintainWebsocketDTO data = new MaintainWebsocketDTO();
                    data.setContent(String.format(WEBSOCKET_CONTENT, cnt));
                    data.setDeptId(deptId);
                    amqpTemplate.convertAndSend(QueueConst.OBD_MAINTAIN_DATA, JSONObject.toJSONString(data));
                }
            }
        } catch (Exception e) {
            XxlJobHelper.handleFail("执行定时任务报错.error:" + e.getMessage());
        }
    }


    /**
     * 获取所有的检测规则
     *
     * @return
     */
    private Map<Integer, List<MaintainRuleDTO>> getCheckRules() {
        Map<Integer, List<MaintainRuleDTO>> map = new HashMap<>();
        List<MaintainRuleDTO> ruleList = maintainRuleMapper.selectAllEnableRule();
        for (MaintainRuleDTO maintainRuleDTO : ruleList) {
            Integer deptId = maintainRuleDTO.getDeptId();
            if (map.containsKey(deptId)) {
                map.get(deptId).add(maintainRuleDTO);
            } else {
                List<MaintainRuleDTO> value = new ArrayList();
                value.add(maintainRuleDTO);
                map.put(deptId, value);
            }
        }
        return map;
    }


    /**
     * 检测是否要保养
     *
     * @param list
     * @param machDTO
     * @param ruleList
     */
    private void check(List<AppMaintain> list, AppMachDTO machDTO, List<MaintainRuleDTO> ruleList) {
        Date maintainTime = maintainMapper.selectLatestMaintainTime(machDTO.getMachId(), null);

        Double totalWorkHours = obdDataMgr.getObdTotalWorkHour(machDTO.getMachId());
        for (MaintainRuleDTO maintainRuleDTO : ruleList) {
            Integer ruleType = maintainRuleDTO.getRuleType();
            Integer value = maintainRuleDTO.getValue();
            MaintainRuleType maintainRuleType = MaintainRuleType.parseValue(ruleType);
            if (maintainRuleType == null) {
                continue;
            }

            AppMaintain appMaintain = new AppMaintain();
            Date now = new Date();
            BeanUtils.copyProperties(machDTO, appMaintain);
            appMaintain.setContent(String.format(maintainRuleType.getDetail(), value));
            appMaintain.setTriggerTime(now);

            // 检测是否需要首次保养
            if (maintainTime == null && MaintainRuleType.FIRST_INTERVAL_HOURS.getValue().equals(ruleType) &&
                    totalWorkHours >= value) {
                // 插入
                list.add(appMaintain);
                return;
            }

            // 按工作小时间隔检测
            if (maintainTime != null && MaintainRuleType.INTERVAL_HOURS.getValue().equals(ruleType)) {
                // 距离上次保养工作时长
                int workTimes = this.getWorkTimes(machDTO.getMachId(), maintainTime, now);
                if (workTimes >= value * 3600) {
                    list.add(appMaintain);
                    return;
                }
            }

            // 按间隔天数检测
            if (maintainTime != null && MaintainRuleType.INTERVAL_DAYS.getValue().equals(ruleType)) {
                int days = DateUtil.getDaysBettweenDate(maintainTime, new Date());
                if (days >= value) {
                    // 插入
                    list.add(appMaintain);
                    return;
                }
            }
        }

    }

    /**
     * 获取距离上次保养的工作时长
     *
     * @param machId
     * @param startTime
     * @param endTime
     * @return
     */
    private int getWorkTimes(Integer machId, Date startTime, Date endTime) {
        //原始数据
        List<AppDeviceObdLogDTO> logList = appDeviceObdLogDao.selectObdLogByMachId(machId, startTime, endTime);
        //硬件工时分段
        List<AppMachDaySegDTO> segList = MachSegmentUtil.separateMachDaySegment(logList);
        //计算工时工时
        int workTimes = 0;
        for (AppMachDaySegDTO segDTO : segList) {
            if (MachUtil.isRunState(segDTO.getWorkState())) {
                workTimes += segDTO.getTimes();
            }
        }
        return workTimes;
    }
}
